package post

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/cachex"
	"mall/common/globalkey"
	"mall/common/jwt"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/blog/rpc/blogclient"
	"mall/service/forum/api/internal/lib/forumreaction"
	"mall/service/forum/api/internal/lib/userrank"
	"mall/service/forum/api/internal/logic/util"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/forum/rpc/forumclient"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetPostByIdLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetPostByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPostByIdLogic {
	return &GetPostByIdLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// 香评详情 ##25.04.01## 历史遗留文件，暂不处理
func (l *GetPostByIdLogic) GetPostById(req *types.ForumPostIdReq) (*types.PostInfo, error) {
	if req.Id == nil || *req.Id == 0 {
		return nil, resultx.NewErrCode(resultx.FORUM_POST_ID_REQUIRED)
	}

	userId := jwt.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	preload := "forumPost:Id,AuthorId,ParentId,AverageRating,Sticky"
	eagerLoad := "forumPost:forumMedia,forumPostMeta"
	preload += "AuthorName,AuthorAvatar,AuthorLevel,Status"
	preload += "ContentHtml,CommentStatus,CommentCount,CreatedAt;forumMedia:Id,Uid,Url,Thumbnail,Order"
	preload += ";forumPostMeta:grade,note,season"
	platformId := globalkey.ThirdPartyPlatformWechatMini

	var searchLogId *uint64
	if req.SearchId != nil && *req.SearchId > 0 {
		searchLogId = pointy.GetPointer(uint64(*req.SearchId))
	}

	res, err := l.svcCtx.ForumRpc.GetPostById(l.ctx, &forumclient.BaseIdReq{
		Id:          req.Id,
		Preload:     pointy.GetPointer(preload),
		EagerLoad:   pointy.GetPointer(eagerLoad),
		VisitorId:   pointy.GetPointer(userId),
		PlatformId:  pointy.GetPointer(platformId),
		SearchLogId: searchLogId,
	})
	if err != nil {
		return nil, err
	}

	if res == nil || res.Id == nil || res.ParentId == nil || *res.Id == 0 || *res.ParentId == 0 {
		return nil, resultx.NewErrCode(resultx.FORUM_POST_STATUS_INVALID)
	}

	//
	parent, err := l.getParentPerfume(*res.ParentId)
	if err != nil {
		return nil, err
	}

	// var private bool = true
	// if res.AuthorId != nil && *res.AuthorId > 0 && *res.AuthorId != userId {
	// 	private = false
	// }

	var photos []*types.MediaInfo
	if len(res.Media) > 0 {
		for _, v := range res.Media {
			var url string
			if v.Url != nil && len(*v.Url) > 0 {
				url = *v.Url
			}
			if len(url) == 0 && v.Uri != nil && len(*v.Uri) > 0 {
				url = *v.Uri
			}
			var thumbnail *bool
			if v.Thumbnail != nil {
				thumbnail = pointy.GetPointer(*v.Thumbnail > 0)
			}
			photos = append(photos, &types.MediaInfo{
				Uid:       v.Uid,
				Id:        v.Id,
				Url:       pointy.GetPointer(url),
				Thumbnail: thumbnail,
				AltText:   v.AltText,
				Details:   v.MetaData,
				Order:     v.Order,
			})
		}
	}

	var meta []*types.MetaInfo
	var notes []*types.MetaInfo
	var grades []*types.MetaInfo
	if len(res.Meta) > 0 {
		var noteIds []string
		var tmpNotes []*types.MetaInfo
		for _, v := range res.Meta {
			if v.Key == nil || v.Value == nil || len(strings.TrimSpace(*v.Key)) == 0 || len(strings.TrimSpace(*v.Value)) == 0 {
				continue
			}

			key := strings.TrimSpace(*v.Key)
			val := strings.TrimSpace(*v.Value)
			if strings.HasPrefix(key, "Grade:") {
				grades = append(grades, &types.MetaInfo{
					Id:    v.Id,
					Key:   pointy.GetPointer(strings.TrimPrefix(key, "Grade:")),
					Value: pointy.GetPointer(val),
				})
			} else if strings.HasPrefix(key, "Note:") {
				key = strings.TrimPrefix(key, "Note:")
				vals := strings.Split(val, ":")
				var label *string
				var slug *string
				if len(vals) >= 2 && len(vals[1]) > 0 && vals[1] != "undefined" {
					label = pointy.GetPointer(vals[1])
					val = vals[1]
					if len(vals[0]) > 0 && vals[0] != "undefined" {
						slug = pointy.GetPointer(vals[0])
					}
				} else if len(vals) == 1 && len(vals[0]) > 0 {
					label = pointy.GetPointer(vals[0])
					val = vals[0]
				}
				noteIds = append(noteIds, key)
				tmpNotes = append(tmpNotes, &types.MetaInfo{
					Id:    v.Id,
					Key:   pointy.GetPointer(key),
					Value: pointy.GetPointer(val),
					Label: label,
					Slug:  slug,
				})
			} else {
				meta = append(meta, &types.MetaInfo{
					Id:    v.Id,
					Key:   pointy.GetPointer(key),
					Value: pointy.GetPointer(val),
				})
			}
		}

		if len(noteIds) > 0 {
			noteList, err := l.getNoteList(noteIds)
			if err != nil {
				return nil, err
			}

			if len(noteList) > 0 {
				noteMap := make(map[string]*blogclient.BookDetailResp)
				mediaMap := make(map[string]*blogclient.BlogMedia)
				for _, v := range noteList {
					noteIdKey := strconv.FormatInt(v.Id, 10)
					if len(v.Media) > 0 {
						mediaMap[noteIdKey] = v.Media[0]
					}
					noteMap[noteIdKey] = v
				}
				for _, v := range tmpNotes {
					if note, ok := noteMap[*v.Key]; ok {
						if len(note.Title) > 0 {
							v.Label = pointy.GetPointer(note.Title)
						}
						if len(note.Slug) > 0 {
							v.Slug = pointy.GetPointer(note.Slug)
						}
					}
					if m, ok := mediaMap[*v.Key]; ok {
						if len(m.Url) > 0 {
							v.Url = pointy.GetPointer(m.Url)
						}
					}
					notes = append(notes, v)
				}
			} else {
				notes = tmpNotes[:]
			}
		}
	}

	var author *types.AuthorInfo
	userRankMap := l.getUserRankMap(userId)
	var levelName *string
	var levelUrl *string
	if userRankMap != nil && res.Author != nil && res.Author.Level != nil && *res.Author.Level > 0 {
		if userRank, ok := userRankMap[*res.Author.Level]; ok {
			levelName = userRank.Name
			levelUrl = userRank.MediaUrl
		}
	}
	if res.Author != nil && res.Author.Name != nil {
		var anonymous *bool
		if res.Author.Anonymous != nil {
			anonymous = pointy.GetPointer(*res.Author.Anonymous > 0)
		}
		author = &types.AuthorInfo{
			Id:        res.Author.Id,
			Name:      res.Author.Name,
			Avatar:    res.Author.Avatar,
			Ip:        res.Author.Ip,
			Level:     res.Author.Level,
			Anonymous: anonymous,
			LevelName: levelName,
			LevelUrl:  levelUrl,
		}
	}
	var isLiked *bool
	var likeCount *uint64
	flag, count := l.getPostLikeInfo(strconv.FormatInt(int64(*res.Id), 10), strconv.FormatInt(int64(userId), 10))
	isLiked = pointy.GetPointer(flag)
	likeCount = pointy.GetPointer(uint64(count))
	var statusText *string
	if res.Status != nil {
		statusText = pointy.GetPointer(util.GetStatusText(int32(*res.Status)))
	}
	var commentStatus *bool
	if res.CommentStatus != nil {
		commentStatus = pointy.GetPointer(*res.CommentStatus > 0)
	}
	var createdAt *string
	if res.CreatedAt != nil && len(*res.CreatedAt) > 0 {
		createdAt = pointy.GetPointer(util.GetTimeFromNow(*res.CreatedAt))
	}

	return &types.PostInfo{
		Id:            res.Id,
		ContentHtml:   res.ContentHtml,
		AuthorId:      res.AuthorId,
		ParentId:      res.ParentId,
		Parent:        parent,
		Photos:        photos,
		Notes:         notes,
		Grades:        grades,
		Meta:          meta,
		Grade:         res.AverageRating,
		Status:        res.Status,
		StatusText:    statusText,
		IsLiked:       isLiked,
		LikeCount:     likeCount,
		CommentStatus: commentStatus,
		CommentCount:  res.CommentCount,
		Author:        author,
		CreatedAt:     createdAt,
		UpdatedAt:     res.CreatedAt,
	}, nil
}

func (l *GetPostByIdLogic) getParentPerfume(id uint64) (*types.KbEntryInfo, error) {
	var preload string = "blogBook:Id,Title,Slug;blogMedia:Id,Uid,Url,Thumbnail;blogBookMeta:year;blogBookRelation:brand"
	var eagerLoad string = "blogBook:blogMedia,blogBookMeta,blogTerm,blogBookRelation"
	res, err := l.svcCtx.BlogRpc.BookDetail(l.ctx, &blogclient.BookDetailReq{
		Id:        int64(id),
		Preload:   preload,
		EagerLoad: eagerLoad,
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("GetPostById getParentPerfume err: %+v", err))
		return nil, err
	}

	var media []*types.TmpMediaInfo
	if len(res.Media) > 0 {
		for _, v := range res.Media {
			media = append(media, &types.TmpMediaInfo{
				Uid:       pointy.GetPointer(uint64(v.Uid)),
				Id:        pointy.GetPointer(strconv.FormatInt(int64(v.Id), 10)),
				Url:       pointy.GetPointer(v.Url),
				Thumbnail: pointy.GetPointer(uint32(v.Thumbnail)),
				AltText:   pointy.GetPointer(v.AltText),
				MetaData:  pointy.GetPointer(v.Meta),
				Order:     pointy.GetPointer(uint32(v.Order)),
			})
		}
	}
	var brand []*types.KbEntryInfo
	if len(res.Relations) > 0 {
		for _, v := range res.Relations {
			if v.Section == "brand" {
				brand = append(brand, &types.KbEntryInfo{
					Id:    pointy.GetPointer(uint64(v.Id)),
					Title: pointy.GetPointer(v.Title),
					Slug:  pointy.GetPointer(v.Slug),
				})
			}
		}
	}
	var year *string
	if len(res.Meta) > 0 {
		for _, v := range res.Meta {
			switch v.Key {
			case "year":
				year = pointy.GetPointer(v.Value)
			}
		}
	}

	return &types.KbEntryInfo{
		Id:     pointy.GetPointer(uint64(res.Id)),
		Title:  pointy.GetPointer(res.Title),
		Slug:   pointy.GetPointer(res.Slug),
		Year:   year,
		Media:  media,
		Brands: brand,
	}, nil
}

func (l *GetPostByIdLogic) getNoteList(ids []string) ([]*blogclient.BookDetailResp, error) {
	preload := "blogBook:Id,Title,Slug;blogMedia:Id,Uid,Url,Thumbnail"
	eagerLoad := "blogBook:blogMedia"
	res, err := l.svcCtx.BlogRpc.BookList(l.ctx, &blogclient.BookListReq{
		Preload:   preload,
		EagerLoad: eagerLoad,
		EntryIds:  strings.Join(ids, ","),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("GetPostById getNoteList err: %+v", err))
		return nil, err
	}

	if len(res.Data) > 0 {
		return res.Data, nil
	}

	return nil, nil
}

func (l *GetPostByIdLogic) getPostLikeInfo(postId string, userId string) (isLiked bool, likeCount int64) {
	forumReaction := forumreaction.NewForumReaction(l.ctx, l.svcCtx, cachex.ForumReviewLike)
	count, err := forumReaction.Count(postId)
	if err != nil {
		return
	}

	likeCount = count
	if count > 0 && len(userId) > 0 {
		isLiked, _ = forumReaction.Done(postId, userId)
	} else if count < 0 {
		likeCount = 0
	}

	return
}

func (l *GetPostByIdLogic) getUserRankMap(userId uint64) map[uint32]*types.UserRankInfo {
	userRank := userrank.NewUserRank(l.ctx, l.svcCtx)
	userRankList, err := userRank.List(userId)
	if err != nil {
		logrus.Info(fmt.Sprintf("GetPostById getUserRankMap err: %+v", err))
		return nil
	}

	if len(userRankList) > 0 {
		resp := make(map[uint32]*types.UserRankInfo)
		for _, v := range userRankList {
			if v.Id != nil && *v.Id > 0 {
				resp[uint32(*v.Id)] = v
			}
		}

		return resp
	}

	return nil
}
